class Compiler {
  // 定义el，vm私有成员
  constructor (vm) {
    this.el=vm.$el
    this.vm=vm
    // 立即调用
    this.compile(this.el)
  }
  // 编译模板，处理文本节点和元素节点
  compile (el) {
    let childNode=el.childNodes
    Array.from(childNode).forEach(node=>{
      if (this.isTextNode(node)){
        this.compilerText(node)
      }else if (this.isElementNode(node)){
        this.compileElement(node)
      }
      // 判断子节点是否还有嵌套子节点，如果有么久递归调用compiler
      if (node.childNodes&&node.childNodes.length){
        this.compile(node)
      }
    })
  }
  // 编译元素节点，处理指令
  compileElement (node) {
    // 遍历所有属性节点
    Array.from(node.attributes).forEach(attr=>{
      // 获取元素节点名称
      let attrName=attr.name;
      if (this.isDirective(attrName)){
        // 截取v-
        attrName = attrName.substr(2)
        let key=attr.value
        this.update(node,key,attrName)
      }
    })
  }
  update (node,key,attrName) {
    if (attrName.startsWith('on:')){
      const event = attrName.replace('on:', '') // 获取事件名
      return this.onUpdater && this.onUpdater(node, event, key)
    }
    // 通过拼接方法名称的方式调用对应的方法
    let updateFn=this[attrName+'Updater']
    updateFn && updateFn.call(this,node,this.vm[key],key)
  }
  onUpdater (node, event, script) {
    // 如果是函数名的情况，则取调用函数：
    if(/^[^\d]\w+$/.test(script)) {
      return node.addEventListener(event, (e) => this.vm[script](e))
    }
    // 是语句的情况用eval直接执行即可
    node.addEventListener(event, function () {
      eval(script)
    })
  }

  // 文本更新
  textUpdater (node,value,key) {
    node.textContent=value
    new Watch(this.vm,key,(newValue)=>{
      node.textContent=newValue
    })
  }
  // 表单更新
  modelUpdater (node,value,key) {
    node.value=value
    // 添加watch实例，同时通过cb回调渲染最新数据
    new Watch(this.vm,key,(newValue)=>{
      node.value=newValue
    })
    // 给表单添加input事件，然后修改值，达到双向绑定的效果
    node.addEventListener('input',()=>{
      this.vm[key]=node.value
    })
  }
  // html ，获取行内元素，直接插入即可
  htmlUpdater (node,value,key){
    node.innerHTML=value
    new Watch(this.vm,key,newValue=>{
      node.innerHTML=node.newValue
    })
  }

  // 编译文本节点，处理插值表达式
  compilerText (node) {
    // {{  msg  }}
    let reg=/\{\{(.+?)\}\}/
    // 获取文本节点内容
    let value=node.textContent
    if (reg.test(value)){
      // 通过正则的构造函数RegExp获取第一个（）内匹配到的内容，同时去除空格
      let key=RegExp.$1.trim()
      // 将匹配到的值替换为数据
      node.textContent=value.replace(reg,this.vm[key])
      new Watch(this.vm,key,(newValue)=>{
        node.textContent = newValue
      })
    }
  }
  // 判断元素属性是否指令
  isDirective (attrName) {
    return attrName.startsWith('v-')
  }
  // 判断节点是否文本节点
  isTextNode (node) {
    return node.nodeType===3
  }
  // 判断节点是否元素节点
  isElementNode (node) {
    return node.nodeType===1
  }

}
